home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Format CD 48
/
Amiga Format CD48 (1999-12-13)(Future Publishing)(GB)(Track 1 of 2)[!][issue 2000-01].iso
/
-in_the_mag-
/
networking
/
crosspc
/
parpc04
/
hardware
/
src
/
parkern.old
< prev
next >
Wrap
Text File
|
1993-08-31
|
32KB
|
1,081 lines
; Lowlevel routines for PARnet
; 03-JUN-93 First PC version <S.A.Pechler@bdk.tue.nl>
; 03-JUL-93 Fixed bug when transferring odd number of bytes.
; Optimized by putting some counters in registers <S.A.Pechler>
; 07-JUL-93 Bugfix: ES was not set when reading into overflow buffer.
; Deleted leading underscores from all data labels.
; Added ParReadV and ParWriteV functions. <S.A.Pechler>
; 11-AUG-93 Bugfix: When an error occured in parread(), the errornumber was
; set in the wrong register (was BX, had to be AX). <S.A.Pechler>
; 01-SEP-93 Optimized the functions by using block move operations (STOSB).
; Changed the register usage of read_data() and write_data().
;
;
; PARALLEL PORT NETWORK LOW LEVEL ROUTINES
;
; THE CABLE: Connect D7-D0,SEL,POUT, and BUSY across,
; Connect ACK to SEL both locally and across,
; Connect GND lines indicated:
;
; (2-9) D7-D0 ------------ D7-D0 (data)
; (12) POUT ------------ POUT (~req)
; (11) BUSY ------------ BUSY (~ack) PARNET INTERFACE
; (13) SEL --+------+-- SEL ( ctl)
; (10) ACK -/ \- ACK ( irq)
; (18-22) GND ------------ GND
;
;
; The interface has in it's standard layout no pullup resistors
; on the lines. You don't need them when at least one Amiga is
; connected on the line. When connecting PC's only, you need to
; place 1 array of pullup resistors in the interface of the LAST
; machine on the line. Such a pullup is made by connecting all the
; lines shown in the cable above (except for GND) with 4.7 KOhm
; resistors to +5 Volt.
;
; +--+ +-----+ +--+ +--+
; |PC| |Amiga| |PC| |PC|
; +--+ +-----+ +--+ +--+
; | | | |
; +-----+ | +-----+ +-----+
; |Iface| . | |Iface| |Iface|
; +-----+ . | +-----+ +-----+
; | | | | . | ++-+
; \------+-----/ | . +-|PR|
; PARnet cable between PC & Amiga | | | +--+
; (no resistors needed) \-------+------/
; PARnet cable between PC's
; (add pullup resistors)
;
; More than 2 machines on the same line are ofcourse possible.
; Note that you can't connect more than 3 Amiga's (or 2 Amiga's
; and 1 PC) without additional buffers, otherwise you'll blow up
; your CIA's. The software can handle 254 machines.
;
; Parallel port usage:
;
; DATA PORT : used for data output only
; STATUS PORT : used for data & control input
; CONTROL PORT: used for control output
;
; All lines pulled up. Thus, asserted state is a 0. Idle
; state is an undriven (1). Protocol transfers a byte at
; a time. Protocol is ethernet style with a small window
; of error in the line aquisition routine.
;
; Note: Timeouts should be set around a second. Ideally
; defaults should be fixed on faster machines.
;
; LINE description:
;
; PARnet: Input: Output: Description:
; ---------------------------------------------------------
; ~ACK SLCT STROBE hand shake
; ~REQ POUT AUTOLF hand shake
; CTL ACK INIT 1 = special byte, 0 = data byte.
; (for address mark, valid when ~REQ
; goes low. For EOP mark, sample
; when ~REQ goes high)
;
; See the associated routines for the exact low-level protocols.
;
;
; PROTOCOL DESCRIPTION:
;
; HandShake (Reader <- Writer transfer). A Handshake
; sequence transfers TWO bytes of information.
;
; WRITER READER
; |-> place data, ~REQ->0
; | wait for ~REQ->0
; | read data & store
; | set ~ACK->0
; | wait ~ACK->0
; | place data, ~REQ->1
; | wait for ~REQ->1
; | read data & store
; | set ~ACK->1
; | wait ~ACK->1
; |<- LOOP (2 bytes written) LOOP (2 bytes read)
;
;
; Read: (1) Determine if your machine is being addressed
; (~REQ=0, data=myaddress, CTL=1)
; (1) set ~ACK to output and
; (2) Handshake sequence for the address mark, only
; first byte valid.
; (3) Handshake sequence for data util rcv byte
; with CTL = 1 (EOP), byte must == 0.
; (4) Set ~ACK to input
;
; Write: (1) AQUIRE THE NETWORK (see below)
; involves gaining control and then setting the
; CTL and ~REQ to outputs.
;
; Also checks if somebody is writing to us,
; in which case -2 is returned instantaniously
; indicating we should do a ParRead().
;
; (2) Handshake sequence for address mark, send dest
; address (second byte garbage). Note that CTL->1
; *BEFORE* we set ~REQ->0
; (3) Handshake sequence for data bytes
; (4) Handshake sequence for EOP mark (Note that CTL->1
; *AFTER* we get the ~ACK->1 and before release
; ~REQ (->1). Only firstbyte valid and set to 0.
;
; (5) Set ~ACK and ~CTL to inputs
;
; AQUIRE: Line aquisition prevents two people from writing to
; the net at the same time.
;
; * A line is considered aquired if ANY of the 3
; control lines is 0.
; * If network is not aquired:
; - set ~ACK to output and 1
; - bclr ~ACK to 0 and bne success
; (else set ~ACK to input and try again)
; - set data lines to output and place my address
; (Must ]be done before CTL is glitched)
; - set ~CTL to output and 1
; - set CTL to 0 and then 1 (glitch it) to cause
; FLAG interrupt on all other machines
; - set ~REQ to output and 0 (beginning of handshake
; sequence)
; - lastly, release ~ACK by setting it to an input
;
; Note that at all times at least one line is a 0
; so no other machine will attempt to aquire the net.
;
; Note that the destination address is placed on the data
; lines after we have aquired the line but before we glitch
; the CTL line to cause an interrupt. This allows the
; other machines to instantaniously determine who is being
; addressed.
;
; Note that the CTL line gets glitched at the end of a packet
; too for the EOP mark. In this case there is a 0 on the
; data lines so while an interrupt is generated, nobody
; thinks they are being addressed.
;
;
; ASSEMBLING/COMPILING the code:
;
; When assembling, use the following flags (turbo-assembler V2.0):
;
; tasm -mx -t -DMEMMOD=large parkern.asm
; option -zi adds full debug information.
;
; When including the routines in C, use the following prototypes:
;
; typedef unsigned short int16;
; extern void paraddress(int16,int16);
; extern int pardataready(void);
; extern int parreadV();
; extern int parread(unsigned char *,int16);
; extern int parwriteV();
; extern int parwrite(int16, unsigned char *,int16);
;
.MODEL MEMMOD,C
LOCALS
%MACS
.LALL
.DATA
ParNetAddr db 1 ; my PARnet address (default)
LPTData dw 0378h ; LPT data port address (default)
LPTStatus dw 0379h ; LPT status port address (default)
LPTControl dw 037ah ; LPT control port address (default)
ParLLTimeout dw 65535
;should be dl 983025 ; default timeout value (about 1 second?)
DestAddress db 5 ; Destination address for write
Dummybuf db 0 ; dummy buffer
db 0 ; dummy buffer
db 0,0 ; padding
NextRVector db 0 ; Flag: vector to next read buffer present
NextWVector db 0 ; Flag: vector to next write buffer present
.CODE
PUBLIC paraddress,pardataready,parreadV,parread,parwrite,parwriteV
; Parnet interface truth table
;
; 0 = line is 0V
; 1 = line is 5V
; X = don't care
;
;SELIN D3 STROBE | RD RC WD WC | Function
;-----------------+-------------+---------------
; 0 0 X | 0 1 1 1 | Read Data
; 0 1 X | 1 1 1 0 | Write Control
; 1 X 0 | 1 1 0 1 | Write Data
; 1 X 1 | 1 0 1 1 | Read Control
;
; Put the interface in a stable mode by switching it into
; 'Read Control' mode.
;
stable: push ax
push dx
mov dx,LPTControl ;control register 2 (to clock in data & Read Control)
mov al,14h ;all outputs to 1 (INIT is not inverted!), enable IRQ7
out dx,al ;STROBE=1 & SELIN=1 -> read control (stable mode).
pop dx
pop ax
ret
; Set the datalines to 'input' by setting all lines to high.
; (Open collector outputs not active).
;
data_input:
push ax
push dx
mov dx,LPTData ;data register
mov al,0ffh ;datalines high, so
out dx,al ;open collector drivers not active.
mov dx,LPTControl ;control register 2 (to set data)
mov al,15h ;SELIN=1 & STROBE=0 Enable IRQ7
out dx,ax ;write data
call Stable ;be sure to CLOCK it in.
pop dx
pop ax
ret
; Set the control-lines to 'input' by setting all lines to high.
; (Open collector outputs not active).
;
control_input:
push ax
push dx
mov dx,LPTData ;data register
mov al,0ffh ;d3=1, rest don't care
out dx,ax
mov dx,LPTControl ;Control register
mov al,1ch ;all outputs to 1, SELIN to 0 (INIT is not inverted!)
out dx,al ;d3=1 & SELIN=0 -> write control
call Stable ;be sure to CLOCK it in.
pop dx
pop ax
ret
; read data from the cable, put it in AL
read_data: ;buffers must be in stable & data_input mode.
push bx
push dx
mov dx,LPTData ;data register
xor al,al ;bit 3 of AL will set line d3=0
out dx,al ;all datalines to zero.
mov dx,LPTControl ;Control register
mov al,1ch ;SELIN=0, rest high (INIT is not inverted!).
out dx,al ;d3=0 & SELIN=0 -> Read Data.
mov dx,LPTStatus ;status register
in al,dx ;read it
mov bl,al ;save higher bits of data to BL (AL will be changed)
mov dx,LPTControl ;control register
in al,dx ;read it (lower bits of data now in AL)
call stable ;put all buffers in tri-state mode.
call data_input ;a bug, the change from RD->RC could activate WD.
; Translation incomming data via control lines.
; (some lines are inverted, and I can't read 8 bits at once)
;
; Let's say, all datalines on the cable are high:
;
; databits: 7654 3210
; value: 1111 1111
;
; Then the input from the control registers would look like this:
;
; AL: data read from control register
;
; AL bits: 7654 3210 centronics corresponding
; value: XXXX X100 keyword: databits:
; |||| |||`- strobe d0
; |||| ||`-- auto LF d1
; |||| |`--- init d2
; |||| `---- invalid x
; ````------ invalid x
;
; BL: data read from status register
;
; BL bits: 7654 3210 centronics corresponding
; value: 0111 1XXX keyword: databits:
; |||| |```- invalid x
; |||| `---- error d3
; |||`------ select d4
; ||`------- paper empty d5
; |`-------- ack d6
; `--------- busy d7
;
translate_data:
xor al,03h ;invert bits 0 and 1
and al,07h ;discards bits not needed in AL.
xor bl,80h ;invert bit 7 in BL (busy).
and bl,0f8h ;discard bits not needed in BL.
or al,bl ;merge them together (high bits in BL, lower in AL)
pop dx
pop bx
ret
; Read the controlbits from the cable (busy, pout & sel)
;
; BL: control read from control register 1, represents the REAL line
; status.
;
; BL bits: 7654 3210 centronics: cable: parnet:
; value: 0111 1XXX
; |||| |```- invalid x
; |||| `---- error x
; |||`------ select busy ack
; ||`------- paper empty pout req
; |`-------- ack sel ctl
; `--------- busy x
;
;Output: like BL above, but shifted 4 bits to the right.
;
read_control: ;control lines must be set to input first.
push ax
push cx
push dx
call Stable ;Stable mode = Read_Control mode.
mov dx,LPTStatus ;Status register
in al,dx ;read it
mov cl,04 ;need CL to shift AL
shr al,cl ;shift 4 bits to the right.
mov bl,al ;save it to BL
and bl,07 ;discard bits not needed.
pop dx
pop cx
pop ax
ret ;ready, controlbits in BL.
;-----------------------------------------------------------------------------
; Clear the parnet ack-bit. Leave other control lines high.
; Warning: I can't read my own control-lines back!
;
clear_ack_only:
push bx
push cx
mov bl,0feh ; set parnet ack-bit to 0
call write_control ; write control
pop cx
pop bx
ret
;-----------------------------------------------------------------------------
; Set the parnet ack-bit. Leave other control lines high.
; Warning: I can't read my own control-lines back!
;
Set_ack_all:
push bx
push cx
mov bl,0fh ; set all bits to 1 (including ACK)
call write_control ; write control
pop cx
pop bx
ret
; Write control
;
; BL: controlbits to be written
;
; BL bits: 7654 3210 centronics
; value: XXXX X100 controlbits: keyword:
; |||| |||`- ack strobe
; |||| ||`-- req auto LF
; |||| |`--- ctl init
; |||| `---- x selin
; ````------ x invalid
;
; !The value in BL represents the REAL LINE STATUS, so a bit=0 means the
; !line is on low voltage.
;
; The INIT output is on the parallel card not inverted. This bit will be
; inverted in this procedure, so you don't have to care about it.
;
write_control: ; Interface must be in stable mode!
push ax
push dx
mov dx,LPTData ; data register
mov al,0ffh ; d3=1, rest don't care.
out dx,al ;
mov dx,LPTControl ; control register
mov al,bl ; move control bits to AL (for OUT-instruction)
xor al,0bh ; invert all lines except for INIT (=ctl)
or al,08h ; SELIN = 0 (inverted!)
out dx,al ; SELIN = 0 & d3=1 -> write control.
call Stable
pop dx
pop ax
ret
; Write DATA
;
; AL: data to be written.
; WARNING: AL will be destroyed after write.
;
write_data: ; interface must be in stable mode!
push dx
mov dx,LPTData ; data register
out dx,al ; put it on (not on cable yet).
mov dx,LPTControl ; control register
mov al,15h ; SELIN=1, strobe=0, enable IRQ7
out dx,al ; write data
call Stable ; be sure to 'CLOCK' it in.
pop dx
ret
; (void) paraddress(int16 myaddr,int16 LPTAddress)
;
; Set my ParNet address (1-254) and LPT port address (0378h,03bch or 0278h)
;
; ParNet addresses 0 and 255 are reserved!
;
paraddress PROC
ARG myad:word,lptad:word
mov ax,myad ; my parnet address
mov ParNetAddr,al ; store address
mov ax,lptad ; LPT port address
mov LPTData,ax ; Place data port address
inc ax ; next register is status port
mov LPTStatus,ax
inc ax ; next register is control port
mov LPTControl,ax
call data_input ; data lines high
call control_input ; control lines high
ret
paraddress ENDP
; int = pardataready(void)
;
; Check for data present (e.g. after an IRQ7).
;
; Returns: 1 if packet is probably pending for you
; 0 if line is currently idle
; -1 if packet isn't for you
;
; If line has been aquired but no control address has been
; put on it yet, pardataready() will wait for a control
; address. Thus, after a signal, a single call to
; pardataready() should suffice.
;
pardataready PROC
call data_input ; be sure all lines
call control_input ; are set to input.
.pdstable:
call read_control ; read control in BL
mov cl,bl ; save it
call read_data ; read data in AL
call read_control ; read control in BL
cmp cl,bl
jne .pdstable
; Now, pardataready might be called after the sending machine
; has aquired but before it can assert REQ. However, the
; sending machine has already (guarenteed) placed its address
; on the data port. So while the address matches, loop while
; REQ not asserted.
test bl,02 ; ~REQ asserted?
jz .pd10 ; yes
cmp [ParNetAddr],al ; no, does data match anyway?
je .pdstable ; YES, loop until get ~REQ or
jmp short .pdfail ; data bad.
.pd10:
test bl,04 ; ~REQ is asserted, CTL=1 ?
jz .pdrn ; no, middle of some packet.
cmp [ParNetAddr],al ; yes, my address?
jne .pdrn ; nope
mov ax,1 ; yes, packet (probably) for us.
jmp short .pdend
.pdfail:
test bl,04 ; fail due to ~REQ not asserted.
jz .pdrn ; CTL=0, line busy
xor ax,ax ; line idle.
jmp short .pdend
.pdrn:
mov ax,-1 ; line busy, packet not for me.
.pdend:
ret
pardataready ENDP
;n = parreadV(unsigned char *buf,int16 bytes, buf2,bytes2, ..., NULL, NULL)
;n = parread(unsigned char *buf,int16 bytes)
;
;Read a pending packet.
;
;Returns: n = -1 (1 second timeout, no packet pending)
; n = 0 to bytes-1 (1 second timeout after transmission interrupted)
; n = bytes (success), or n > bytes (transmitting machine's packet
; was larger than we can handle, extra bytes thrown out)
;
;NOTE: Requesting an odd number of bytes is O.K. but if you request N where
; N is odd and the writer sends N + 1 you will never know (N will be
; returned). See also parwrite() below.
;
parreadV PROC
ARG buf:ptr, byts:word, nextbuf: ptr, nextbyts:word
mov al,1
mov NextRVector,al ; secondary buffer(s) present
jmp short parRbegin
parreadV ENDP
parread PROC
ARG buf:ptr, byts:word, nbuf:ptr, nextbyts:word
; ^these fake params are needed for reference.
mov al,0 ; no secondary buffer(s)
mov NextRVector,al
parRbegin:
if @Datasize NE 0
push ds ; the 'USES' macro doesn't work here.
push es
push si
push di
les di,buf ; es:di = buf, my data segment needs to
; stay intact.
else
push ds
push es
push si
push di
mov ax,ds
mov es,ax ; to simulate a mov es,ds
mov di,buf ; es:di = buf (ds already set)
endif
cld ; all moves will be forward.
mov si,byts ; maximum number of bytes to read.
call data_input ; ensure all
call control_input ; are inputs.
call Stable ; ensure line not asserted.
mov DX,[ParLLTimeout] ; DX = timeout load
; Wait loop for address mark
; Ctl = 1, ~DReq = 0
.rmstab:
call read_control ; read control in BL
mov cl,bl ; save it
call read_data ; read data in AL
call read_control ; read control in BL
cmp cl,bl ; control lines stable?
jne .rmstab ; nope
test bl,04 ; expect CTL=1
jz .rms1 ; nope
test bl,02 ; expect ~REQ=0
jz .rms2 ; yes
.rms1:
dec dx ; decrement timeout count
jnz .rmstab ; no timeout.
mov ax,-1 ; return value: read timeout
jmp .rmend ; no address mark!
.rms2:
cmp [ParNetAddr],al ; My address?
jne .rms1 ; no, timeout loop
; Got my address, ~Ack byte.
mov DX,[ParLLTimeout] ; reset timeout
call Clear_Ack_Only ; set ~Ack to 0
.rms4: call read_control ; get control
test bl,02 ; wait for ~REQ to go away
jnz .rms5
dec dx ; decrement timeout count
jnz .rms4
mov ax,-2 ;~REQ not released?
jmp .rmend
.rms5: call Set_Ack_All ; release ~ACK
mov cx,0 ; set # of bytes read to 0
jmp .rms10 ; skip past move
; MAIN READ LOOP
;
; cx holds count (number of bytes read).
; DI buffer pointer (place to store data).
.rms10loop:
; mov es:[di],al ; store data
; inc di ; next address
stosb
.rms10:
call Read_Control
test bl,02 ; wait for ~REQ asserted
jz .rms20
call Read_control
test bl,02 ; again
jz .rms20
mov DX,[ParLLTimeout] ; reset timeout
.rms11: call Read_Control
test bl,02 ; wait for ~REQ asserted with timeout
jz .rms20
dec dx ; decrement timeout count
jnz .rms11
mov ax,-1
jmp short .rmend ; timeout
.rms20: call read_data ; get data in al and
call Clear_Ack_Only ; assert ~ACK
; note on CTL = 1 end sequence this data item is a dummy
; mov es:[di],al ; store data
; inc di ; next address
stosb
inc cx ; optimized, but not quite true,
inc cx ; we've only written one 1 sf.
call Read_Control
test bl,02 ; wait for ~REQ released
jnz .rms30
call Read_Control
test bl,02 ; again
jnz .rms30
mov DX,[ParLLTimeout] ; reset timeout
.rms21:
call Read_Control
test bl,02 ; wait for ~REQ released with timeout
jnz .rms30
dec dx ; decrement timeout count
jnz .rms21 ; no timeout yet?
jmp short .rmendsub ; sub because CX is 2 ahead
.rms30:
call read_data ; get data in al
call Read_Control ; get CTL status in BL
call Set_Ack_All ; release ~ACK
test bl,04 ; EOP if CTL=1
jnz .rmeop
; CANNOT STORE DATA HERE! In case odd # bytes requested,
; second byte would overflow buffer (each handshake sequence ALWAYS
; transfers 2 bytes of information)
dec si ; # of bytes remaining
jz .rmodd ; already zero, # of bytes were odd.
dec si
jz .rmste ; reached zero (even bytes)
jmp .rms10loop ; continue if >0.
.rmodd: dec si ; si must be -1 when odd # bytes requested.
dec cx ; fixup count (was one ahead)
jmp short .rmeven
.rmste:
mov es:[di],al ; if si = 0, its's even and we
; should store the last byte.
.rmeven:
cmp NextRVector,0 ; if next buffer does not exist
je .rmovflow ; overflow
.rmsev0:
cmp WORD PTR nbuf,0 ; next buffer points to somewhere?
if @Datasize NE 0
jne .rmsev1
cmp WORD PTR nbuf+2,0 ; check also the segment
else
je .rmovflow
endif
.rmsev1:
mov si,nextbyts ; length of next buffer
if @Datasize NE 0
les di,nbuf ; load pointer to next buffer
add bp,6 ; this is VERY tricky = sizeof(nbuf+nbyts)
else
mov di,nbuf ; load pointer to next buffer
add bp,4 ; this one is also VERY tricky
endif
or si,si ; size of this buffer = 0 ?
jz .rmsev0 ; yes, then check for next buffer.
jmp .rms10 ; proceed reading
.rmovflow:
if @Datasize NE 0
mov di,ds
mov es,di ; to simulate move es,ds
endif
mov di,OFFSET Dummybuf ; overflow, use dummy buffer
jmp .rms10
.rmeop:
cmp al,0 ; EOP data better be 0!
je .rmendsub
mov ax,-3 ; bad protocol
jmp short .rmend
.rmendsub:
dec cx ; because we were two ahead
dec cx
.rmend:
call data_input
call Set_Ack_All ; setting ~ACK to input
call Stable
pop di
pop si
pop es ; normally the 'USES' macro inserts these.
pop ds
ret ; return value in AL
parread ENDP
;n = parwriteV(int 16 destaddr, unsigned char *buf, int16 bytes,
; buf2, bytes2, ..., NULL, NULL)
;n = parwrite(int16 destaddr, unsigned char *buf, int16 bytes)
;
;Write a packet.
;
;Returns: n = -2 Cannot write anything, a packet is pending
; (instantanious)
; n = -1 Destination machine does not respond (1 sec timeout)
; n = N N bytes written ok (success if n == bytes)
;
; NOTE: sending an odd number of bytes is O.K. but if you write N where
; N is odd and the reader requests N + 1 he will get N + 1 the last
; byte being garbage.
;
parwriteV PROC
ARG dest:word, buf:ptr, byts:word, nextbuf:ptr, nextbyts:word
mov al,1
mov NextWVector,al
jmp short parWbegin
parwriteV ENDP
parwrite PROC
ARG dest:word, buf:ptr, byts:word, nbuf:ptr, nextbyts:word
; ^these fake params are needed for
; reference.
mov al,0
mov NextWVector,al ; no secondary buffer(s)
parWbegin:
if @Datasize NE 0
push ds ; the 'USES' macro doesn't work here.
push es
push si
push di
les si,buf ; es:si = source-buffer to be written
else
push ds
push es
push si
push di
mov ax,ds
mov es,ax ; to simulate a mov es,ds
mov si,buf ; es:si = buf (ds already set)
endif
cld ; all moves will be forward.
mov ax,dest ; destination address.
mov DestAddress,al ; can't do a direct move to DestAddress.
mov di,byts ; number of bytes to write.
call data_input ; ensure all
call control_input ; are inputs.
call Stable ; ensure line not asserted.
mov DX,[ParLLTimeout] ; DX = timeout load
.wmstab:
cli ; disable interrupts
call Read_Control
mov cl,bl
call read_data
call Read_Control ; get stable control
cmp cl,bl
je .wmstab1
sti ; set interrupts
jmp short .wmstab
; Interrupts still disabled
; BL holds ~ACK ~REQ and CTL status
.wmstab1:
mov ax,-2 ; number of bytes written yet (=none).
cmp bl,07h ; ~ACK=1, ~REQ=1, CTL=1 ?
je .wm02
; if CTL=1, ~REQ=0 and AL=my address then
; return with -2
test bl,02h ; ~REQ = 0?
jne .wm01 ; nope
test bl,04h ; CTL = 1?
je .wm01 ; nope
cmp [ParNetAddr],al ; somebody is calling me?
jne .wm01 ; nope
sti ; enable interrupts
jmp .wmend
.wm01:
sti ; enable interrupts
dec dx ; decrement timeout count
jnz .wmstab
jmp .wmend
; interrupts still disabled
; we almost own the line
.wm02:
call Read_Control
call Clear_Ack_Only ; assert ~ACK
test bl,01
jne .wm05 ; ACK was released before, have line!
call Set_Ack_All ; release ~ACK
jmp short .wm01
; Line now aquired.
.wm05:
sti ; enable interrupts
mov al,DestAddress
call Write_Data ; set destination address on datalines.
; Before asserting ~REQ, pulse CTL to cause interrupt on remote
; machines. Note that our address is already on the datalines.
mov bl,02h ; leave ~ACK=0 and ~REQ=1, but CTL->0
call Write_Control
mov bl,06h ; leave ~ACK=0 and ~REQ=1, but CTL->1
call Write_Control
mov bl,04h ; leave ~ACK=0 and CTL=1, but ~REQ->0
call Write_Control ; assert REQ
mov bl,05h ; leave CTL=1 and ~REQ=0, but ~ACK->1
call Write_Control ; release ACK
; (note that REQ->0 before ACK->released)
mov ax,-1 ; number of bytes written yet (none).
; interrupts enabled for transfer (fully handshaked)
;
; Address mark ~ACK, wait for ~ACK asserted.
.wm10:
call Read_Control
test bl,01 ; ~ACK asserted?
jz .wm15 ; yes, remote machine got my address mark
mov DX,[ParLLTimeout] ; DX = timeout count
.wm11:
call Read_Control
test bl,01
jz .wm15
dec dx ; decrement timeout count
jnz .wm11 ; timeout?
jmp .wmend ; yes.
; got ack, now set CTL = 0 (leaves at least one line 0 so
; nobody else thinks the bus is idle!)
;
; note: Since this is the address mark, and is sampled by
; the reader before it asserts ~ACK, I can set CTL
; = 0 now instead of waiting till after ~ACK is
; released.
.wm15:
mov bl,01h ; leave ACK=1, REQ=0, but CTL->0
call Write_Control ; set CTL = 0 for duration of packet
mov bl,03h ; leave ACK=1, CTL=0, but REQ->1
call Write_Control ; release ~REQ
mov cx,0 ; number of bytes written (none).
; Data transfer loop
;
; wait for ~ACK to be released (-> 1). If no more bytes to
; write, then skip to .wm50
.wm20:
or di,di ; more data in this buffer?
jz .wm50 ; nope
call Read_Control
test bl,01 ; wait for ~ACK to be released
jnz .wm30
mov DX,[ParLLTimeout] ; DX = timeout countdown
.wm21:
call Read_Control
test bl,01
jnz .wm30 ; need the timeout here?
call Read_control
test bl,01 ; check ACK again
jnz .wm30
dec dx
jnz .wm21
jmp .wmend ; timeout
; Assert ~REQ for this data byte and wait for ~ACK
.wm30:
mov al,es:[si] ; get next data byte.
inc si ; this can't be done with a LODSB :-(
call Write_Data ; store data and ..
mov bl,01h ; leave ACK=1, CTL=0, but REQ->0
call Write_Control ; .. assert ~REQ
mov al,es:[si] ; get next data bytes
inc si ; this can't be done with a LODSB :-(
inc cx ; number of bytes written (this only)
; (not valid until we get ACK which
; is why the wmendsub is included)
call Read_Control
test bl,01 ; wait for ACK asserted
jz .wm40
call Read_Control
test bl,01 ; look again
jz .wm40
mov DX,[ParLLTimeout] ; DX = timeout count
.wm31:
call Read_Control
test bl,01 ; wait for ACK asserted with timeout
jz .wm40
dec dx ; decrement timeout count
jnz .wm31
jmp short .wmendsub ; timeout, no bytes written!
; have ~ACK, so byte transmitted. increment bytes written,
; decr. of bytes left was already done before.
; now send second byte and loop back.
.wm40: call Write_Data ; data was loaded in AL before.
mov bl,03h ; leave ACK=1, CTL=0, but REQ->1
call Write_Control ; release ~REQ
inc cx ; increment number of bytes written
dec di ; one less bytes
jz .wm40a ; odd number of bytes were written
dec di ; one less bytes
jz .wm50 ; these were the last even bytes(s)
jmp .wm20 ; loop back
.wm40a: dec di ; fixup, di = -1 when odd bytes requested
jmp short .wm50 ; last odd byte, go send an EOP.
; Last byte in buffer has been transmitted.
;
; Get next buffer in vector.
.wm50: cmp NextWVector,0 ; if next buffer does not exist
je .wm50a ; yes.
cmp WORD PTR nbuf,0 ; next buffer points to somewhere?
if @Datasize NE 0
jne .wmnext
cmp WORD PTR nbuf+2,0 ; check also the segment
endif
je .wm50a ; is zero, so no next buffer.
.wmnext:
mov di,nextbyts ; length of next buffer
if @Datasize NE 0
les si,nbuf ; load pointer to next buffer
add bp,6 ; this is VERY tricky = sizeof(nbuf+nbyts)
else
mov si,nbuf ; load pointer to next buffer
add bp,4 ; this one is also VERY tricky
endif
jmp .wm20 ; proceed write.
.wm50a:
; Last byte has been transmitted,
;
; Wait for ~ACK to be released and then assert ~REQ with
; EOP & CTL = 1
;
; (timing on read is that CTL is sampled when ~REQ is
; RELEASED so no timing window here)
call Read_Control ; Wait ~ACK released
test bl,01
jz .wm50
mov al,0
call Write_Data ; EOP mark (=0)
mov bl,01h ; leave ACK=1 and CTL=0, but REQ->0
call Write_Control ; assert ~REQ
; Wait for ~ACK asserted
call Read_Control
test bl,01 ; ACK asserted?
jz .wm60 ; yes
mov DX,[ParLLTimeout] ; DX = timeout count
.wm51:
call Read_Control
test bl,01 ; wait for ACK asserted with timeout
jz .wm60
dec dx ; decrement timeout count
jnz .wm51
mov ax,-3 ; EOP failed
jmp short .wmend
; Set CTL=1 then release ~REQ, then wait for ~ACK released
.wm60:
mov bl,05h ; leave ACK=1, REQ=0, but CTL->1
call Write_Control ; release CTL
mov bl,07h ; leave ACK=1, CTL=1, but REQ->1
call Write_Control ; release ~REQ
; Wait ~ACK released ?
.wm61: call Read_Control
test bl,01
jz .wm61
; Add DI to CX. This handles fixup if an odd number of bytes were
; requested written, DI will be -1 (odd) or 0 (even) and CX will
; be one too large (odd) or perfect (even)
add cx,di
jmp short .wmend1
.wmendsub:
dec cx ; was ahead in count
.wmend1:
mov ax,cx ; return value in AX
.wmend:
call data_input ; set data port to input
call control_input ; set control lines to input
pop di
pop si
pop es
pop ds ; normally done by the 'USES' macro
ret ; return value in AX
parwrite ENDP
END